﻿using System;
using System.Collections.Generic;
using System.Linq;

using Dxr.Police.IACS.Models;

using Zongsoft.Data;
using Zongsoft.Common;
using OfficeOpenXml;

namespace Dxr.Police.IACS.FullText
{
    public class DataSourceBase<T> : IDataSource
        where T : ISourceModel
    {
        private DataType _type;
        private string _insertScope;
        private string[] _searchColumns;
        private string _timeColumn;
        private System.Reflection.PropertyInfo _key;
        private System.Reflection.PropertyInfo _order;
        private System.Reflection.PropertyInfo[] _contents;

        public DataSourceBase()
        {
            var model = System.Activator.CreateInstance<T>();
            var properties = model.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(SourceAttribute), true).Length > 0);
            _type = model.DataType;
            _insertScope = string.Join(",", properties.Where(p => ((SourceAttribute)p.GetCustomAttributes(typeof(SourceAttribute), true)[0]).Skip).Select(p => "!" + p.Name));
            _searchColumns = properties.Where(p => ((SourceAttribute)p.GetCustomAttributes(typeof(SourceAttribute), true)[0]).Condition).Select(p => p.Name).ToArray();
            var timeP = properties.FirstOrDefault(p => ((SourceAttribute)p.GetCustomAttributes(typeof(SourceAttribute), true)[0]).TimeLine);
            _timeColumn = timeP == null ? "" : timeP.Name;
            TypeEntry = _type.GetEnumEntry();

            _key = properties.FirstOrDefault(p => ((SourceAttribute)p.GetCustomAttributes(typeof(SourceAttribute), true)[0]).Key);
            _order = properties.FirstOrDefault(p => ((SourceAttribute)p.GetCustomAttributes(typeof(SourceAttribute), true)[0]).Order);
            _contents = properties.Where(p => ((SourceAttribute)p.GetCustomAttributes(typeof(SourceAttribute), true)[0]).Show).OrderBy(p => ((SourceAttribute)p.GetCustomAttributes(typeof(SourceAttribute), true)[0]).ShowIndex).ToArray();
        }

        [Zongsoft.Services.ServiceDependency]
        public IDataAccess DataAccess
        { get; set; }

        public EnumEntry TypeEntry
        {
            get; private set;
        }

        public bool CanUse(DataType type)
        {
            return _type == type;
        }

        public virtual int ImportFromExcel(string path, DataType type, Dxr.Police.Models.UserInfo user)
        {
            var result = 0;

            var models = ReadExcel(path);

            if (models != null && models.Any())
            {
                using (var tranc = new Zongsoft.Transactions.Transaction())
                {
                    foreach (var item in models)
                    {
                        result += Insert(item, user);
                    }

                    tranc.Commit();
                }
            }

            return result;
        }

        public IEnumerable<FullTextModel> Select(FullTextConditional condition, Paging paging)
        {
            var conditional = BuildCondition(condition);

            return DataAccess.Select<T>(condition: conditional, paging: paging, sortings: new Sorting(SortingMode.Descending, _order.Name)).Select(p =>
                {
                    var temp = ResultBuild(p);
                    OnResultBuilded(p, temp);

                    temp.Type = _type;
                    temp.TypeName = TypeEntry.Description;
                    temp.TypeSubName = TypeEntry.Alias;

                    return temp;
                });
        }

        protected virtual void OnInserting(T model, Dxr.Police.Models.UserInfo user) { }

        protected virtual void OnResultBuilded(T t, FullTextModel model) { }

        private FullTextModel ResultBuild(T model)
        {
            return new FullTextModel()
            {
                Id = _key.GetValue(model).ToString(),
                OrderTime = (DateTime)_order.GetValue(model),
                Content = _contents.Select(p =>
                {
                    return new FullTextContentItem()
                    {
                        Key = ((SourceAttribute)p.GetCustomAttributes(typeof(SourceAttribute), true)[0]).Name,
                        Value = p.GetValue(model).ToString()
                    };
                })
            };
        }

        private ICondition BuildCondition(FullTextConditional model)
        {
            var condition = new ConditionCollection(ConditionCombination.And);

            if (model.Start.HasValue && model.End.HasValue && !string.IsNullOrEmpty(_timeColumn))
            {
                condition.Add(Condition.GreaterThanEqual(_timeColumn, model.Start.Value));
                condition.Add(Condition.LessThan(_timeColumn, model.End.Value));
            }

            var keys = model.Text.Split(' ');
            var temp = new ConditionCollection(ConditionCombination.Or);

            foreach (var item in _searchColumns)
                temp.AddRange(keys.Select(p => Condition.Like(item, $"%{p}%")));

            condition.Add(temp);

            return condition;
        }

        private int Insert(T model, Dxr.Police.Models.UserInfo user)
        {
            OnInserting(model, user);
            return DataAccess.Insert<T>(model, _insertScope);
        }

        private IEnumerable<T> ReadExcel(string path)
        {
            var columns = new List<System.Reflection.PropertyInfo>();
            var notNull = new List<bool>();
            var list = new List<T>();
            var properties = typeof(T).GetProperties();
            using (ExcelPackage package = new ExcelPackage(new System.IO.FileStream(path, System.IO.FileMode.Open)))
            {
                ExcelWorksheet sheet = package.Workbook.Worksheets.FirstOrDefault();
                for (int m = sheet.Dimension.Start.Row + 1, n = sheet.Dimension.End.Row; m <= n; m++)
                {
                    var head = sheet.GetValue<string>(m, 1);
                    if (head == "序号" || head == "字段描述" || head == "备注" || head == "注意事项")
                        continue;
                    if (head == "字段名称")
                    {
                        for (int j = sheet.Dimension.Start.Column + 1, k = sheet.Dimension.End.Column; j <= k; j++)
                            columns.Add(properties.FirstOrDefault(p => string.Equals(p.Name, sheet.GetValue(m, j).ToString(), StringComparison.OrdinalIgnoreCase)));

                        continue;
                    }

                    if (head == "是否必采项")
                    {
                        for (int j = sheet.Dimension.Start.Column + 1, k = sheet.Dimension.End.Column; j <= k; j++)
                            notNull.Add(!string.IsNullOrEmpty(sheet.GetValue<string>(m, j)));

                        continue;
                    }

                    var hasAdd = true;
                    var model = System.Activator.CreateInstance<T>();

                    for (int j = sheet.Dimension.Start.Column + 1, k = sheet.Dimension.End.Column; j <= k; j++)
                    {
                        var p = columns[j - 2];

                        if (p == null)
                            continue;

                        var value = sheet.GetValue(m, j);

                        if (notNull[j - 2] && (value == null || string.IsNullOrEmpty(value.ToString())))
                        {
                            hasAdd = false;
                            break;
                        }

                        if (value == null)
                            continue;

                        p.SetValue(model, value.ToString());
                    }

                    if (hasAdd)
                        list.Add(model);
                }
            }
            return list;
        }
    }
}
